/*
power key 按键中断
同步阻塞方式
 

 在button_drv1 基础上加入 tasklet 下半部处理
   
*/


#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/gpio.h> 										   
#include <linux/cdev.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>

#include "button.h"



//定义button的设备结构体(类)
struct rk_button {
	unsigned int major;
	dev_t devno;
    struct class *cls;
    struct device *dev;
	unsigned int gpio_button; //保存引脚号
	int irqno; //保存中断号
	int value; //保存button_write接收数据
	struct cdev *cdev;
	struct button_event event; //按键的事件对象
	wait_queue_head_t wq_head; //等待队列头对象
	int have_data; //表示设备是否有数据 1--有  0--无
	struct tasklet_struct	tasklet; 
};

static struct rk_button *button_device;

//定义一个下半部处理函数
void button_tasklet_func(unsigned long data)
{
	printk("<kernel> data=%ld\n", data);
	//当设备有数据产生时,唤醒阻塞的进程

	//修改休眠的条件
	button_device->have_data = 1;
	//从等待队列唤醒	
	wake_up_interruptible(&button_device->wq_head);
	printk("<kernel> %s pid=%d  name=%s\n", __func__, current->pid, current->comm);
	printk("<kernel>call %s()\n", __func__); 
}




//定义中断处理函数
static irqreturn_t button_handler(int irqno, void *dev)
{
	
	//读引脚的电平
	int value = gpio_get_value(button_device->gpio_button);
	button_device->event.code = 'A';
	if (value)
		button_device->event.value = 0;
	else
		button_device->event.value = 1;

	//调度下半部
	tasklet_schedule(&button_device->tasklet);
	
	printk("<kernel> %s pid=%d  name=%s\n", __func__, current->pid, current->comm);

	printk("<kernel>call %s()\n", __func__); 
	return IRQ_HANDLED;
}


//交给驱动框架来调用的回调函数
static int button_open (struct inode *inode, struct file *filp)
{
	printk("<kernel>call %s()\n", __func__); 
	return 0;
}

static int button_close (struct inode *inode, struct file *filp)
{
	printk("<kernel>call %s()\n", __func__); 
	return 0;

}


ssize_t button_read(struct file *filp, char __user *buf, size_t size, loff_t *flag)
{
	int ret;

	printk("<kernel> %s pid=%d  name=%s\n", __func__, current->pid, current->comm);

	//判断open的打开方式是否非阻塞,设备是否有数据
	
	if (filp->f_flags & O_NONBLOCK && !button_device->have_data)
		return -EAGAIN; //非阻塞返回的错误码
	

	//判断设备是否有数据可用,条件不满足就休眠
	wait_event_interruptible(button_device->wq_head, button_device->have_data);

	ret = copy_to_user(buf, &button_device->event, size);
	if (ret) {
		printk("copy_to_user fail\n");
		return -EINVAL;
	}
	memset(&button_device->event, 0, sizeof(button_device->event));
	button_device->have_data = 0; //数据读取了
	printk("<kernel>call %s()\n", __func__); 
	return 0;

}

static struct file_operations fops = { //只部分初始化自己用的
	.owner = THIS_MODULE, //当前模块绑定
	.open = button_open,
	.read = button_read,
	.release = button_close,

};

static int __init button_init(void)
{
	int ret;
	//创建button设备对象

	button_device = kmalloc(sizeof(struct rk_button), GFP_KERNEL);
	if (!button_device) {
		printk("kmalloc fail\n");
		return -ENOMEM;
	}
	
#if 0
	//动态申请设备号
	button_device->major = register_chrdev(0, "button_drv", &fops);
	if (button_device->major < 0) {
		printk("<kernel>register_chrdev fail\n");
		ret = -EBUSY;
		goto err_register_chrdev;

	}
	printk("<kernel> major=%d\n", button_device->major);
#endif
	//新方法: 申请设备号
	ret = alloc_chrdev_region(&button_device->devno, 64, 1, "hello_drv"); //baseminor=64 count=1
	if (ret) {
		printk("alloc_chrdev_region fail\n");
		goto err_alloc_chrdev_region;
	}
	
	button_device->major = MAJOR(button_device->devno); //通过设备号拿主设备号	
	printk("<kernel> major=%d\n", button_device->major);

	//新方法: 注册cdev对象
	//创建
	button_device->cdev = cdev_alloc();
	//初始化
	cdev_init(button_device->cdev, &fops);

	//注册
	cdev_add(button_device->cdev, button_device->devno, 1);

	//创建设备类

	button_device->cls = class_create(THIS_MODULE, "button_cls");
	if (IS_ERR(button_device->cls)) {			
		printk("couldn't create class\n");	
		
		ret = PTR_ERR(button_device->cls);	
		goto err_class_create;
	}			
	

	//创建设备对象==创建设备文件
	button_device->dev = device_create(button_device->cls, NULL, button_device->devno, NULL,  "button"); // /dev/button
	if (IS_ERR(button_device->dev)) { 											
		printk("device_create fail\n");
		ret = PTR_ERR(button_device->dev); 	
		goto err_device_create;
	}


	//硬件初始化
	button_device->gpio_button = 5; //GPIO0_A5

	//获取中断号
	button_device->irqno = gpio_to_irq(button_device->gpio_button);
	printk("<kernel> irqno=%d  gpiono=%d\n", button_device->irqno, button_device->gpio_button);
	//注册中断
	ret = request_irq(button_device->irqno, button_handler, IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING, "mypower-key", NULL);
	if (ret) {
		printk("request_irq fail\n");
		ret = -EINVAL;
		goto err_request_irq;

	}
	//清空事件对象
	memset(&button_device->event, 0, sizeof(button_device->event));

	//初始化等待队列头
	init_waitqueue_head(&button_device->wq_head);
	button_device->have_data = 0; //设备无数据

	//初始化tasklet对象
	tasklet_init(&button_device->tasklet, button_tasklet_func, 123);


	
	printk("<kernel>call %s()\n", __func__);  
	return 0;

//集中错误处理
err_request_irq:
	device_destroy(button_device->cls, button_device->devno);
err_device_create:
	class_destroy(button_device->cls);
err_class_create:
	unregister_chrdev_region(button_device->devno, 1);
err_alloc_chrdev_region:
	kfree(button_device);
	return ret;

	
}


static void __exit button_exit(void)
{
	free_irq(button_device->irqno, NULL);
	device_destroy(button_device->cls, button_device->devno);
	class_destroy(button_device->cls);
	unregister_chrdev_region(button_device->devno, 1);
	kfree(button_device);
	printk("<kernel>call %s()for oop @ %d\n", __FUNCTION__, __LINE__);  
	
}

module_init(button_init); 
module_exit(button_exit); 
MODULE_LICENSE("GPL"); 



